
/*
 * En este archivo implementamos todas las funciones definidas en
 * el archivo entradasalida.h
 *
 * Estas mismas funciones son usadas en la versión paralela.
 */

#include "entradasalida.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>


/* Definimos algunas constantes */

#define MAX_LONG_ARCHIVO 255
#define MAX_LONG_FECHA 40
#define MAX_LONG_VALOR 500


/* Prototipado de funciones locales */

struct VECTOR* obtenerVectorAleatorio();
struct VECTOR* obtenerVectorPorArchivo();
struct VECTOR* obtenerVectorPorTeclado();

int leerValorInt();
double leerValorDouble();
char leerValorChar();
void leerValorText(char *valueBuffer);

void limpiarTexto(char *texto);
int archivoExistente(char *nombreArchivo);
void guardarContenidoVector(char *nombreArchivo, struct VECTOR *vector);


/*
 * Implementación de la función definida en cabecera.
 */
struct VECTOR* obtenerVectorDelUsuario() {

    // Aqui referenciaremos el vector obtenido
    struct VECTOR *vector;

    // Variable para determinar si debemos volver a pedir los datos al usuario.
    int pedirDatos = 1;
    int guardarVector; // Indica si debemos guardar el vector obtenido

    while (pedirDatos == 1) {

        // Mostramos los mensajes correspondientes
        printf("Para el ingreso de datos desea:\n");
        printf("1) Ingresar los datos por teclado\n");
        printf("2) Obtener los datos por archivo\n");
        printf("3) Generar los datos aleatoriamente\n");
        printf("Seleccione una opción: ");

        // Obtenemos lo que ingresó el usuario
        char opcionSeleccionada = leerValorChar();

        // En base a lo que ingresó el usuario, decidimos que hacer.
        switch(opcionSeleccionada) {

            case '1':

                // Hay que pedirle al usuario que ingrese el vector por teclado
                vector = obtenerVectorPorTeclado();
                guardarVector = 1;
                pedirDatos = 0;
                
                break;

            case '2':

                // Hay que leer el vector desde un archivo
                vector = obtenerVectorPorArchivo();
                guardarVector = 0;
                pedirDatos = 0;

                break;

            case '3':

                // Hay que generar un vector aleatorio
                vector = obtenerVectorAleatorio();
                guardarVector = 1;
                pedirDatos = 0;

                break;

            default:

                // No seleccionó nada válido, no validamos ni guardamos.
                printf("No seleccionó una opción válida ...\n\n");
                guardarVector = 0;

                break;
                
        }

    }

    // Si es necesario guardar el vector, lo guardamos
    if(guardarVector) {
        almacenarVector(vector, "vector ingresado");
    }

    // Devolvemos el vector generado
    return vector;

}


/*
 * Implementación de la función definida en cabecera.
 */
void almacenarVector(struct VECTOR *vector, char *nombreVector){

    // Preguntamos al usuario si desea guardar el vector
    printf("¿Desea almacenar el/la %s? (s/n): ", nombreVector);
    
    char respuesta; // Acá almacenaremos la respuesta del usuario
    int pedirRespuesta; // Variable usada para volver a pedir una respuesta

    do {

        // Pedimos una respuesta válida
        respuesta = leerValorChar();

        // Segun lo que respondió el usuario tomamos una acción
        switch (respuesta) {

            // Si desea guardar el vector
            case 's': {

                int pedirNombre;// Variable de condicion de salida del bucle

                do {

                    // Obtener el nombre del archivo
                    char nombreArchivo[MAX_LONG_ARCHIVO];
                    printf("Especifique un nombre para el archivo: ");
                    leerValorText(nombreArchivo);

                    // Verificar que el archivo no exista
                    if(archivoExistente(nombreArchivo)) {

                        printf("Error. Ya existe un archivo con el nombre indicado.\n");
                        pedirNombre = 1;

                    } else {

                        // Guardamos efectivamente el vector
                        guardarContenidoVector(nombreArchivo, vector);
                        pedirNombre = 0; // Salimos del bucle

                    }

                } while (pedirNombre);

                printf("Vector guardado exitósamente.\n");
                pedirRespuesta = 0;// Permitimos la salida del bucle
                break;

            }

            // Si no desea guardar el vector
            case 'n': {

                printf("No guardamos el vector.\n");
                pedirRespuesta = 0; // Permitimos la salida del bucle
                break;

            }

            // Cualquier otra respuesta
            default: {

                // Imprimimos un mensaje de error y indicamos que se pida otra respuesta
                printf("Respuesta inválida, por favor responda 's' o 'n': ");
                pedirRespuesta = 1;
                break;

            }

        }

    } while (pedirRespuesta);

}


/*
 * Le solicita al usuario que ingrese el vector por teclado.
 * Primero solicita el tamanio del vector.
 * Luego solicita cada elemento del vector.
 */
struct VECTOR *obtenerVectorPorTeclado() {

    struct VECTOR *vector; // Aqui guardamos el vector obtenido.
    int dimension = 0; // Aqui guardaremos la cantidad de elementos del vector

    // Vamos a obtener la dimensión del vector
    int pedirDimension = 1;

    do {

        printf("\nIngrese la cantidad de elementos del vector: ");
        dimension = leerValorInt();

        // Chequeamos que la dimensión sea mayor o igual que 1.
        if (dimension >= 1) {

            vector = crearVector(dimension); // Reservamos memoria para el vector
            int *elementos = vector->elementos; // Buffereamos los elementos del vector

            // Ahora pedimos los elementos uno a uno.
            int i;
            for (i=0; i<dimension; i++) {

                // Solicitamos el elemento actual
                printf("Ingrese el valor %d : ",i+1);
                elementos[i] = leerValorInt();

            }

            // Permitimos la salida del bucle.
            pedirDimension = 0;

        } else { // Dimensión incorrecta
            printf("Error: La dimensión del vector debe ser mayor que 0.");
        }

    } while(pedirDimension);

    // Devolvemos el vector obtenido
    return vector;

}


/*
 * Lee un vector desde un archivo.
 * Primero solicita el nombre del archivo y luego procede a leer el vector
 * desde ese archivo.
 */
struct VECTOR *obtenerVectorPorArchivo() {

    FILE *filePointer; // Referencia al archivo a leer
    int pedirOtroArchivo = 1; // Variable de condicion del bucle
    struct VECTOR *vector; // Aqui guardaremos el vector a devolver

    // Primero obtenemos el nombre del archivo e intentamos abrirlo
    do {

        char nombreArchivo[MAX_LONG_ARCHIVO]; // Aqui guardamos el nombre del archivo
        printf("Ingrese el nombre del archivo: ");
        leerValorText(nombreArchivo);

        // Abrimos el archivo especificado
        filePointer = fopen(nombreArchivo,"r");

        // Si el archivo se pudo abrir continuamos, sino salimos del bucle
        if (filePointer != NULL) {

            // Trataremos de leer el tamaño
            int dimension;
            char resultadoLectura; // Resultado de la llamada a fscanf
            resultadoLectura = fscanf(filePointer, "dimension = %d\n", &dimension);

            if(resultadoLectura == EOF) { // Si el archivo está vacío
                printf("El archivo está vacío.\n");
            } else if(resultadoLectura != 1) { // Si no se leyó el tamaño
                printf("No se ha especificado un tamaño en el archivo.\n");
            } else if (dimension < 1) { // Si la dimensión leída es inválida
                printf("El archivo contiene un vector vacío.\n");
            } else {

                // Creamos el vector a leer
                vector = crearVector(dimension);

                // Tratamos de leer todos los valores
                int contadorValores = 0;
                char dummyChar; // Usado para leer valores basura

                while(!feof(filePointer)) {

                    // Indica el valor y posición del elemento que leemos en esta iteración
                    int valorActual;
                    int posicion;
                    resultadoLectura = fscanf(
                            filePointer, "vector[%d] = %d\n", &posicion, &valorActual);

                    // Solo asignamos los valores leidos si se leyeron las 2 variables
                    if(resultadoLectura == 2) {
                        vector->elementos[posicion] = valorActual; // Asignamos el elemento leído
                        contadorValores++;
                    } else {
                        fread(&dummyChar, sizeof(char), 1, filePointer);
                    }

                }

                // Cerramos el archivo
                fclose(filePointer);

                // Determinamos si se leyeron todos los valores necesarios
                if(contadorValores == dimension) {
                    pedirOtroArchivo = 0;
                    printf("Vector leído exitósamente\n");
                } else {
                    printf("El archivo indicado no posee un vector válido\n");
                }
                
            }

        } else {
            printf("El archivo no se pudo abrir o no existe.\n");
        }

    } while (pedirOtroArchivo);

    // Devolvemos el vector obtenido
    return vector;
    
}


/*
 * Genera un vector con valores aleatorios.
 * El tamaño del vector es solicitado al usuario.
 */
struct VECTOR *obtenerVectorAleatorio() {

    struct VECTOR *vector; // Aqui guardamos el vector obtenido.
    long unsigned int dimension = 0; // Aqui guadaremos el tamaño del vector.

    // Vamos a obtener el tamaño del vector
    int pedirDimension = 1;

    do {

        printf("\nIngrese la cantidad de elementos del vector: ");
        dimension = leerValorInt();

        // Chequeamos que la dimensión sea mayor o igual que 1.
        if (dimension >= 1) {

            vector = crearVector(dimension); // Reservamos memoria para el vector
            int i; // Variables de iteracion
            
            for(i=0; i<dimension; i++) {
                vector->elementos[i] = (rand() % 1000);
            }

            // Permitimos la salida del bucle.
            pedirDimension = 0;

        } else { // Dimensión incorrecta
            printf("Error: La dimensión del vector debe ser mayor que 0.");
        }

    } while(pedirDimension);

    // Devolvemos el vector obtenido
    return vector;

}


/*
 * Esta función obtiene una cadena desde el teclado.
 */
void leerValorText(char *valueBuffer) {

    int pedirValor; // Usaremos esta variable para saber si debemos volver a pedir el valor

    do { // Iteremos para pedir el valor

        // Leemos una cadena completa del teclado y la limpiamos
        char *buffer = fgets(valueBuffer, MAX_LONG_VALOR, stdin);
        limpiarTexto(buffer);

        // Ahora chequeamos lo que se ingresó
        if(buffer == NULL) {

            pedirValor = 1; // Si la llamada retornó null, no se ingresó nada válido

        } else if (strlen(buffer)==0) {

            printf("\tCadena vacía, ingrese nuevamente: ");
            pedirValor = 1;

        } else {
            pedirValor = 0;
        }

    } while (pedirValor);

}


/*
 * Esta función obtiene un caracter desde el teclado.
 * La función no retorna hasta obtener un caracter válido.
 */
char leerValorChar() {

    int pedirValor; // Usaremos esta variable para saber si debemos volver a pedir el valor
    char valueBuffer[MAX_LONG_VALOR];// Buffer del valor ingresado por teclado
    char valorObtenido; // Aqui guardaremos el valor a devolver

    do { // Iteremos para pedir el valor

        // Leemos una cadena completa del teclado y la limpiamos
        char *buffer = fgets(valueBuffer, MAX_LONG_VALOR, stdin);
        limpiarTexto(buffer);

        // Ahora chequeamos lo que se ingresó
        if(buffer == NULL) {

            pedirValor = 1; // Si la llamada retornó null, no se ingresó nada valido

        } else if (strlen(buffer)!=1) {

            printf("\tValor incorrecto, ingrese nuevamente: ");
            pedirValor = 1;

        } else {
            valorObtenido = buffer[0];
            pedirValor = 0;
        }

    } while (pedirValor);

    // Retornamos el valor obtenido
    return valorObtenido;

}


/*
 * Esta función obtiene un número entero desde el teclado.
 * La función no retorna hasta obtener un número válido.
 */
int leerValorInt() {

    int pedirValor; // Usaremos esta variable para saber si debemos volver a pedir el valor
    char valueBuffer[MAX_LONG_VALOR];// Buffer del valor ingresado por teclado
    int valorObtenido; // Aqui guardaremos el valor a devolver

    do { // Iteremos para pedir el valor

        // Leemos una cadena completa del teclado y la limpiamos
        fflush(stdin);
        char *buffer = fgets(valueBuffer, MAX_LONG_VALOR, stdin);
        limpiarTexto(buffer);

        // Ahora chequeamos lo que se ingresó
        if(buffer == NULL) {

            pedirValor = 1; // Si la llamada retornó null, no se ingresó nada válido

        } else if (!strcmp(buffer, "0")) {

            valorObtenido = 0;  // El valor ingresado es 0, devolvemos el número 0.
            pedirValor = 0;     // Ya tenemos el valor, no seguir iterando

        } else {

            // Ahora debemos convertir la cadena ingresada a un número.
            valorObtenido = atoi(buffer);

            // Si devuelve 0, el valor obtenido es erróneo.
            if(valorObtenido == 0) {
                printf("\tValor incorrecto, ingrese nuevamente: ");
                pedirValor = 1;
            } else {
                pedirValor = 0;
            }

        }

    } while (pedirValor);

    // Retornamos el valor obtenido
    return valorObtenido;

}


/*
 * Esta función solicita al usuario que ingrese un valor del tipo double.
 * La función no retorna hasta obtener un valor válido.
 */
double leerValorDouble() {

    int pedirValor; // Usaremos esta variable para saber si debemos volver a pedir el valor
    char valueBuffer[MAX_LONG_VALOR];// Buffer del valor ingresado por teclado
    double valorObtenido; // Aqui guardaremos el valor a devolver

    do { // Iteremos para pedir el valor

        // Pedir al usuario un valor y limpiarlo
        fflush(stdin);
        char *buffer = fgets(valueBuffer, MAX_LONG_VALOR, stdin);
        limpiarTexto(buffer);

        // Ahora chequeamos lo que se ingresó
        if(buffer == NULL) {

            pedirValor = 1; // Si la llamada retornó null, no se ingresó nada válido

        } else if (!strcmp(buffer, "0")) {

            valorObtenido = 0;  // El valor ingresado es 0, devolvemos el número 0.
            pedirValor = 0;     // Ya tenemos el valor, no seguir iterando

        } else {

            // Ahora debemos convertir la cadena ingresada a un número.
            valorObtenido = atof(buffer);

            // Si devuelve 0, el valor obtenido es erróneo.
            if(valorObtenido == 0) {
                printf("\tValor incorrecto, ingrese nuevamente: ");
                pedirValor = 1;
            } else {
                pedirValor = 0;
            }

        }

    } while (pedirValor);

    // Retornamos el valor obtenido
    return valorObtenido;

}


/*
 * Esta función toma un texto como parámetro y elimina todos los caracteres
 * no imprimibles del mismo.
 */
void limpiarTexto(char *texto) {

    // Calculamos la longitud total texto a limpiar
    int longTotal = strlen(texto);

    // Utilizaremos esta cadena como buffer para construir el texto limpio
    char *textoBuffer = (char *) malloc(sizeof(char) * longTotal + 1);

    // Iteramos sobre la cadena
    int i, j; // j sera usada como indice de escritura para el buffer
    j = 0;

    for (i=0; i<longTotal; i++) {

        // Si el caracter no es un caracter de control, lo escribirmos en el buffer
        if(isprint(texto[i])) {
            textoBuffer[j] = texto[i];
            j++;
        }

    }

    // Escribimos el fin de cadena en el buffer
    textoBuffer[j] = 0;

    // Copiamos el buffer en la cadena original
    strcpy(texto, textoBuffer);

    // Vaciamos el buffer
    free(textoBuffer);

}


/*
 * Esta funcion verifica que no exista un archivo con el nombre especificado
 */
int archivoExistente(char *nombreArchivo) {

    // Intentamos abrir un archivo con el nombre especificado
    FILE *filePointer = fopen(nombreArchivo, "r");

    // Si la apertura fue exitosa, entonces el archivo existe
    if(filePointer != NULL) {

        fclose(filePointer);
        return 1;
        
    } else {
        return 0;
    }

}


/*
 * Esta funcion se encarga de guardar todo el contenido de objeto VECTOR
 * en un archivo con el nombre especificado.
 */
void guardarContenidoVector(char *nombreArchivo, struct VECTOR *vector) {

    // Creamos y abrimos el archivo
    FILE *filePointer = fopen(nombreArchivo, "w");

    // Escribimos la dimension
    fprintf(filePointer, "dimension = %d\n", vector->dimension);

    // Ahora escribimos los elementos individuales
    int i;
    for(i=0; i<vector->dimension; i++) {
        fprintf(filePointer, "vector[%d] = %d\n", i, vector->elementos[i]);
    }

    // Cerramos el archivo
    fclose(filePointer);

}
